package org.anachronos.jeep.ui.trace;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.anachronos.jeep.model.trace.Invocation;
import org.anachronos.jeep.model.trace.util.TraceSwitch;
import org.eclipse.jface.viewers.ICellEditorValidator;

public abstract class TraceFilterParser implements ICellEditorValidator {
	public class TraceFilterSwitch extends TraceSwitch<Boolean> {
	}
	private static final class NumericTraceFilterParser extends
			TraceFilterParser {
		private final static Pattern NUMERIC_PATTERN = 
			Pattern.compile("\\s*(<|>|=)?\\s*(\\d*)");
		private class FilterSwitch extends TraceFilterSwitch {
			private final Long filterNumber;
			private final int compareTo;
			private final TraceColumnSwitch columnSwitch = new TraceColumnSwitch();
			
			public FilterSwitch(final Long filterNumber, final int compareTo, int columnIndex) {
				this.filterNumber = filterNumber;
				this.compareTo = compareTo;
				columnSwitch.setColumnIndex(columnIndex);
			}
			
			@Override
			public Boolean caseInvocation(Invocation object) {
				final Comparable<?> columnElement = columnSwitch.doSwitch(object);
				final Long number;
				if (columnElement instanceof Double) {
					Double timePercent = (Double) columnElement;
					number = (long) (timePercent * 100);
				} else {
					number = ((Long) columnElement) / 1000;
				}
				final int compare = filterNumber.compareTo(number);
				final boolean select = compare == compareTo;
				return select;
			}
		}
		
		@Override
		public String isValid(final Object value) {
			return NUMERIC_PATTERN.matcher(value.toString()).matches() ? null : "Inavlid filter expression!";
		}

		@Override
		public TraceFilterSwitch parse(final String filter, final int columnIndex) {
			final Matcher matcher = NUMERIC_PATTERN.matcher(filter);
			if (matcher.matches()) {
				try {
					final String filterNumberAsString = matcher.group(2);
					final String relation = matcher.group(1);
					final int compareTo;
					if ("<".equals(relation)) {
						compareTo = 1;
					} else if ("=".equals(relation) || relation == null) {
						compareTo = 0;
					} else {
						compareTo = -1;
					}

					final Long filterNumber = (Long) NumberFormat.getIntegerInstance().parse(filterNumberAsString);
					return new FilterSwitch(filterNumber, compareTo, columnIndex);
				} catch (final ParseException e) {
					// can't happen since pattern matches!
				}
			} 
			return null;
		}
	}
	private static class NameTraceFilterParser extends TraceFilterParser {
		private class FilterSwitch extends TraceFilterSwitch {
			private final TraceColumnSwitch columnSwitch = new TraceColumnSwitch();
			private final Pattern namePattern;
			
			public FilterSwitch(final Pattern namePattern, final int columnIndex) {
				this.namePattern = namePattern;
				this.columnSwitch.setColumnIndex(columnIndex);
			}
			
			@Override
			public Boolean caseInvocation(Invocation object) {
				final String name = (String) columnSwitch.doSwitch(object);
				final boolean matches = namePattern.matcher(name).matches();
				return matches;
			}
		}
		
		@Override
		public String isValid(final Object value) {
			return null;
		}

		@Override
		public TraceFilterSwitch parse(final String filter, final int columnIndex) {
			final String replacedAsterix = filter.replaceAll("\\*", ".*");
			final String namePatternString = replacedAsterix.replaceAll("([A-Z])", "$1.*")
				.replaceAll("([^A-Z])([A-Z])", "$1.*$2").concat(".*");
			final Pattern namePattern = Pattern.compile(namePatternString);
			return new FilterSwitch(namePattern, columnIndex);
		}		
	}
	public static final TraceFilterParser NUMERIC = new NumericTraceFilterParser();
	public static final TraceFilterParser NAME = new NameTraceFilterParser();
	
	public abstract TraceFilterSwitch parse(String filter, int columnIndex);
}
